﻿//==============================================================================================
//Author: Uncle Song
//Create Date: 2022-06-8
//Description: 线网格创建
//----------------------------------------------------------------------------------------------
//Alter History:
//              2022-06-08  add Codes.
//              2022-12-1  修改2d线重面问题
//              2023-5-30  添加3d立体线
//============================================================================================== 
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
namespace FrameWorkSong
{
    public class MeshLineCreate
    {
        class LinePoint
        {
            Vector3 location;
            Vector3 direction;

            public Vector3 Location { get => location; set => location = value; }
            public Vector3 Direction { get => direction; set => direction = value; }
        }

        class LinePoint2D :LinePoint
        {
            float lineWidth;
            public float LineWidth { get => lineWidth; set => lineWidth = value; }
        }
        #region 面片线生成
        /// <summary>
        /// 面片线生成
        /// </summary>
        /// <param name="createPoint">线点数组</param>
        /// <param name="lineWidth">线宽</param>
        /// <param name="type">线的轴放向默认0</param>
        /// <returns></returns>
        public Mesh CreateLine2D(Vector3[] createPoint, float lineWidth, int type = 0)
        {
            List<LinePoint2D> linePoints = SetLinePoint2D(createPoint, lineWidth);
            Vector3[] meshPoint = Create2DMeshPoint(linePoints, type);
            return CreatMesh(ToLinePoint(linePoints), meshPoint, 1, 1);
        }
        #region 轴心线建立
        /// <summary>
        /// 轴心线的建立
        /// </summary>
        /// <param name="createPoint"></param>
        /// <param name="lineWidth"></param>
        /// <returns></returns>
        List<LinePoint2D> SetLinePoint2D(Vector3[] createPoint, float lineWidth)
        {
            List<LinePoint2D> linePoint = new List<LinePoint2D>();
            int length = createPoint.Length;
            for (int i = 0; i < length; i++)
            {
                if (i == 0)
                {
                    Add2DLinePoints(lineWidth, createPoint[i], (createPoint[i + 1] - createPoint[i]).GetPlaneVector().normalized, ref linePoint);
                }
                else if (i == length - 1)
                {
                    Add2DLinePoints(lineWidth, createPoint[i], (createPoint[i] - createPoint[i - 1]).GetPlaneVector().normalized, ref linePoint);
                }
                else
                {
                    Get2DElbowPoint(createPoint[i], createPoint[i - 1], createPoint[i + 1], lineWidth, ref linePoint);
                }
            }

            return linePoint;
        }
        /// <summary>
        /// 增加线点
        /// </summary>
        /// <param name="location"></param>
        /// <param name="direction"></param>
        void Add2DLinePoints(float lineWidth, Vector3 location, Vector3 direction, ref List<LinePoint2D> pipePoints)
        {
            LinePoint2D linePoint = new LinePoint2D();
            linePoint.LineWidth = lineWidth;
            linePoint.Location = location;
            linePoint.Direction = direction;
            pipePoints.Add(linePoint);
        }
        /// <summary>
        /// 得到弯点
        /// </summary>
        /// <param name="focusPoint">中间点</param>
        /// <param name="frontPoint">前一点</param>
        /// <param name="backPoint">后一点</param>
        void Get2DElbowPoint(Vector3 focusPoint, Vector3 frontPoint, Vector3 backPoint, float lineWidth, ref List<LinePoint2D> linePoint)
        {
            //焦点前后向量
            Vector3 frontVec = (frontPoint - focusPoint).normalized;
            Vector3 backVec = (backPoint - focusPoint).normalized;
            //平面点
            Vector3 frontPlaneVec = (frontPoint - focusPoint).GetPlaneVector().normalized;
            Vector3 backPlaneVec = (backPoint - focusPoint).GetPlaneVector().normalized;
            Vector3 focusVec = (frontPlaneVec + backPlaneVec).normalized;

            //得到前后断点（这里不加断点的，uv会变形，不考虑uv可以不加）
            Vector3 tangencyFrontPoint = (lineWidth * frontVec * 0.5f) + focusPoint;
            Vector3 tangencyBackPoint = (lineWidth * backVec * 0.5f) + focusPoint;

            //得到两个焦点向量的法线
            Vector3 normalVec = Vector3.Cross(frontVec, backVec).normalized;
            Vector3 directionVec;
            //前后向量相反时，法线向量为零
            if (frontPlaneVec == -backPlaneVec)
            {
                directionVec = backPlaneVec;
            }
            else
            {
                directionVec = -Vector3.Cross(focusVec, normalVec).normalized;
            }
            Add2DLinePoints(lineWidth, tangencyFrontPoint, -frontPlaneVec, ref linePoint);
            float angle = Vector3.Angle(frontPlaneVec, backPlaneVec) * 0.5f;
            if (angle > 30)
            {
                float newLineWidth = lineWidth / Mathf.Sin(angle * Mathf.Deg2Rad);
                //增加管线点
                Add2DLinePoints(newLineWidth, focusPoint, directionVec, ref linePoint);
            }
            Add2DLinePoints(lineWidth, tangencyBackPoint, backPlaneVec, ref linePoint);
        }
        #endregion

        #region 创建网格点
        Vector3[] Create2DMeshPoint(List<LinePoint2D> linePoint, int type)
        {
            int length = linePoint.Count;
            Vector3[] meshPoint = new Vector3[length * 2];
            for (int i = 0; i < length; i++)
            {
                if (type == 0)
                {
                    meshPoint[(i * 2) + 0] = new Vector3(linePoint[i].LineWidth * 0.5f, 0, 0).FromToMoveRotation(linePoint[i].Location, linePoint[i].Direction);
                    meshPoint[(i * 2) + 1] = new Vector3(-linePoint[i].LineWidth * 0.5f, 0, 0).FromToMoveRotation(linePoint[i].Location, linePoint[i].Direction);
                }
                else if (type == 1)
                {
                    meshPoint[(i * 2) + 0] = new Vector3(0, 0, linePoint[i].LineWidth * 0.5f).FromToMoveRotation(linePoint[i].Location, linePoint[i].Direction);
                    meshPoint[(i * 2) + 1] = new Vector3(0, 0, -linePoint[i].LineWidth * 0.5f).FromToMoveRotation(linePoint[i].Location, linePoint[i].Direction);
                }
                else
                {
                    meshPoint[(i * 2) + 0] = new Vector3(0, linePoint[i].LineWidth * 0.5f, 0).FromToMoveRotation(linePoint[i].Location, linePoint[i].Direction);
                    meshPoint[(i * 2) + 1] = new Vector3(0, -linePoint[i].LineWidth * 0.5f, 0).FromToMoveRotation(linePoint[i].Location, linePoint[i].Direction);
                }
            }
            return meshPoint;
        }
        #endregion
        #endregion

        #region 立体线建立
        /// <summary>
        /// 立体网格创建
        /// </summary>
        /// <param name="createPoint">创建的点数据</param>
        /// <param name="circularCount">圆的段数</param>
        /// <param name="circularR">圆的半径</param>
        /// <returns></returns>
        public Mesh CreateLine3D(Vector3[] createPoint, int circularCount, float circularR)
        {
            Vector3[] circul = CircularSection(circularCount, circularR);
            List<LinePoint> centreLine = SetLinePoint(createPoint);
            Vector3[] meshPoint = CreateMeshPoint(centreLine, circul);
            float uvX = Vector3.Distance(circul[0], circul[1]);
            return CreatMesh(centreLine, meshPoint, circul.Length, uvX);
        }
        /// <summary>
        /// 中心线的确立
        /// </summary>
        /// <param name="createPoint">曲线点</param>
        /// <returns></returns>
        List<LinePoint> SetLinePoint(Vector3[] createPoint)
        {
            List<LinePoint> pipePoints = new List<LinePoint>();
            int length = createPoint.Length;
            for (int i = 0; i < length; i++)
            {
                if (i == 0)
                {
                    Vector3 tangent = (createPoint[i + 1] - createPoint[i]).normalized;//切线
                    AddPipePoints(createPoint[i], tangent, ref pipePoints);
                }
                else if (i == length - 1)
                {
                    Vector3 tangent = (createPoint[i] - createPoint[i - 1]).normalized;//切线
                    AddPipePoints(createPoint[i], tangent, ref pipePoints);
                }
                else
                {
                    Vector3 tangent = (createPoint[i + 1] - createPoint[i - 1]).normalized;//切线
                    AddPipePoints(createPoint[i], tangent, ref pipePoints);
                }
            }
            return pipePoints;
        }
        #endregion

        #region 管线线建立
        /// <summary>
        /// 创建管线
        /// </summary>
        /// <param name="createPoint">管线点数组</param>
        /// <param name="circularCount">管线段数</param>
        /// <param name="circularR">管线半径</param>
        /// <param name="elbowR">弯头半径</param>
        /// <returns></returns>
        public Mesh CreateLine3D(Vector3[] createPoint, int circularCount, float circularR, float elbowR)
        {

            Vector3[] circul = CircularSection(circularCount, circularR);
            List<LinePoint> pipePoints = SetPipePoint(createPoint, elbowR);
            //画出中心线
            DrawGizmos drawGizmos= DrawGizmos.Instance;
      

            //画出切线
            List<Vector3> vector3s=new List<Vector3>();
      
            for (int i = 0; i < pipePoints.Count; i++)
            {
                vector3s.Add(pipePoints[i].Location);
                vector3s.Add(pipePoints[i].Location+ (pipePoints[i].Direction*2));
            }
            drawGizmos.CurvePoints = vector3s;
            drawGizmos.LineColor = Color.red;
            drawGizmos.IsPoint = false;
            //
            Vector3[] meshPoint = CreateMeshPoint(pipePoints, circul);
            float uvX = Vector3.Distance(circul[0], circul[1]);
            return CreatMesh(pipePoints, meshPoint, circul.Length, uvX);

        }

        #region 横切圆创建
        /// <summary>
        /// 得到管线横切圆
        /// </summary>
        /// <param name="Count">段数</param>
        /// <param name="R">半径</param>
        /// <returns></returns>
        Vector3[] CircularSection(int Count, float R)
        {
            Vector3[] vector3s = new Vector3[Count];
            float angle = 360 / Count;
            Vector3 vector3 = new Vector3(R, 0, 0);
            for (int i = 0; i < Count; i++)
            {
                //根据角度得到圆的分布点
                vector3s[i] = vector3.ToAngle(angle * i, Vector3.zero, Vector3.forward);
            }
            return vector3s;
        }
        #endregion

        #region 得到3d线的轴心路径
        /// <summary>
        /// 设置中心路径
        /// </summary>
        /// <param name="createPoint"></param>
        /// <returns></returns>
        List<LinePoint> SetPipePoint(Vector3[] createPoint, float elbowR)
        {
            List<LinePoint> pipePoints = new List<LinePoint>();
            int length = createPoint.Length;
            for (int i = 0; i < length; i++)
            {
                if (i == 0)
                {
                    Vector3 tangent = (createPoint[i + 1] - createPoint[i]).normalized;//切线
                    AddPipePoints(createPoint[i], tangent, ref pipePoints);
                }
                else if (i == length - 1)
                {
                    Vector3 tangent = (createPoint[i] - createPoint[i - 1]).normalized;//切线
                    AddPipePoints(createPoint[i], tangent, ref pipePoints);
                }
                else
                {
                    GetElbowPoint(createPoint[i], createPoint[i - 1], createPoint[i + 1], elbowR, ref pipePoints);
                }
            }
            return pipePoints;
        }
        /// <summary>
        /// 增加管线点
        /// </summary>
        /// <param name="location"></param>
        /// <param name="direction"></param>
        void AddPipePoints(Vector3 location, Vector3 direction,  ref List<LinePoint> pipePoints)
        {
            LinePoint pipePoint = new LinePoint();
            pipePoint.Location = location;
            pipePoint.Direction = direction;
            pipePoints.Add(pipePoint);
        }
        /// <summary>
        /// 得到弯头点
        /// </summary>
        /// <param name="focus"></param>
        /// <param name="front"></param>
        /// <param name="back"></param>
        /// <param name="r">内切圆半径</param>
        void GetElbowPoint(Vector3 focus, Vector3 front, Vector3 back, float r, ref List<LinePoint> pipePoints)
        {
            //焦点前后向量
            Vector3 frontVec = focus - front;
            Vector3 backVec = back - focus;
            float angle_fo = Vector3.Angle(-frontVec, backVec) / 2;
            //得到前后切点
            Vector3 tangencyFront = ((frontVec.magnitude - r) * frontVec.normalized) + front;
            Vector3 tangencyBack = (r * backVec.normalized) + focus;
            //Vector3 tangencyFront = ((frontVec.magnitude - r / Mathf.Tan(angle_fo * Mathf.Deg2Rad)) * frontVec.normalized) + front;
            //Vector3 tangencyBack = (r / Mathf.Tan(angle_fo * Mathf.Deg2Rad) * backVec.normalized) + focus;
            //得到内切圆圆心
            Vector3 circulPoint = GetCirculPoint(focus, tangencyFront, tangencyBack);
            //得到弯头分段
            Vector3[] circulSection = GetCirculSection(focus, tangencyFront, tangencyBack, circulPoint);
            //得到两个焦点向量的法线
            Vector3 normal = Vector3.Cross(frontVec, backVec).normalized;
            //增加管线点
            AddPipePoints(tangencyFront, GetDirection(tangencyFront, circulPoint, normal), ref pipePoints);
            int length = circulSection.Length;
            for (int i = 0; i < length; i++)
            {
                AddPipePoints(circulSection[i], GetDirection(circulSection[i], circulPoint, normal), ref pipePoints);
            }
            AddPipePoints(tangencyBack, GetDirection(tangencyBack, circulPoint, normal), ref pipePoints);
        }
        /// <summary>
        /// 得到弯点在内切圆上的切线方向
        /// </summary>
        /// <param name="self"></param>
        /// <param name="circulPoint"></param>
        /// <param name="normal"></param>
        /// <returns></returns>
        Vector3 GetDirection(Vector3 self, Vector3 circulPoint, Vector3 normal)
        {
            Vector3 vector = circulPoint - self;
            return Vector3.Cross(vector, normal).normalized;
        }
        /// <summary>
        /// 得到管线弯头内切圆分段点
        /// </summary>
        /// <param name="focus">交点</param>
        /// <param name="tangency1">切点1</param>
        /// <param name="tangency2">切点2</param>
        /// <param name="circulPoint">切圆圆心</param>
        /// <returns></returns>
        Vector3[] GetCirculSection(Vector3 focus, Vector3 tangency1, Vector3 tangency2, Vector3 circulPoint)
        {
            Vector3 vector0 = tangency1 - circulPoint;
            Vector3 vector4 = tangency2 - circulPoint;
            float dis = vector0.magnitude;
            Vector3 vector2 = (vector0 + vector4).normalized * dis;
            Vector3 vector1 = (vector0 + vector2).normalized * dis;
            Vector3 vector3 = (vector4 + vector2).normalized * dis;
            Vector3[] vector3s = new Vector3[3];
            vector3s[0] = vector1 + circulPoint;
            vector3s[1] = vector2 + circulPoint;
            vector3s[2] = vector3 + circulPoint;
            return vector3s;
        }
        /// <summary>
        /// 得到管线弯头内切圆圆心
        /// </summary>
        /// <param name="focus">交点</param>
        /// <param name="tangency1">切点1</param>
        /// <param name="tangency2">切点2</param>
        /// <returns></returns>
        Vector3 GetCirculPoint(Vector3 focus, Vector3 tangency1, Vector3 tangency2)
        {
            Vector3 vector1 = tangency1 - focus;
            Vector3 vector2 = tangency2 - focus;
            Vector3 vector0 = (vector1 + vector2).normalized;
            float angle = Vector3.Angle(vector1, vector0);
            float dis = vector1.magnitude / Mathf.Cos(angle * Mathf.Deg2Rad);
            return (vector0 * dis) + focus;
        }
        #endregion

        #region 创建网格点
        Vector3[] CreateMeshPoint(List<LinePoint> linePoint, Vector3[] circular)
        {
            int length = linePoint.Count;
            int circularCount = circular.Length;
            Vector3[] meshPoint = new Vector3[length * circularCount];
            for (int i = 0; i < length; i++)
            {
                for (int j = 0; j < circularCount; j++)
                {
                    meshPoint[(i * circularCount) + j] = circular[j].FromToMoveRotation(linePoint[i].Location, linePoint[i].Direction);
                }
            }
            return meshPoint;
        }
        #endregion
        #endregion

        #region 网格创建
         List<LinePoint> ToLinePoint( List<LinePoint2D> LinePoint2D)
        {
            List<LinePoint> linePoint = new List<LinePoint>();
            for (int i = 0; i < LinePoint2D.Count; i++)
            {
                linePoint.Add(LinePoint2D[i]);
            }
            return linePoint;
        }
        /// <summary>
        /// 网格创建
        /// </summary>
        /// <param name="linePoints">线的轴心线组</param>
        /// <param name="meshPoint">网格点</param>
        /// <param name="count">段数</param>
        /// <param name="uvX">uv宽度</param>
        /// <returns></returns>
        Mesh CreatMesh(List<LinePoint> linePoints, Vector3[] meshPoint, int count, float uvX)
        {
            Mesh mesh = new Mesh();
            mesh.vertices = meshPoint;
            mesh.triangles = GetTriangles(linePoints.Count, count);
            mesh.uv = GetUV(linePoints, count, uvX);
            mesh.RecalculateNormals();
            mesh.RecalculateBounds();
            return mesh;
        }

        Vector2[] GetUV(List<LinePoint> linePoints,int count, float uvX)
        {
            int length = linePoints.Count;
            if (count == 1) { count = 2; }
            Vector2[] uvs = new Vector2[(count * length)];
            float lineDis = 0;
            int k = 0;
            for (int i = 0; i < length; i ++)
            {
                
                if (i != 0)
                {
                    lineDis += Vector3.Distance(linePoints[i].Location, linePoints[i - 1].Location);
                }
                for (int j = 0; j < count; j++)
                {
                    Vector2 vector2;
                    if (j % 2 != 0)
                    {
                        vector2 = new Vector2(uvX, lineDis);
                    }
                    else
                    {
                        vector2 = new Vector2(0, lineDis);
                    }

                    uvs[k] = vector2;
                    k += 1;
                }
            }
            return uvs;
        }
        /// <param name="length">线段段数</param>
        /// <param name="count">横截面段数（也就是圆的段数）</param>
        /// <returns></returns>
        int[] GetTriangles(int length, int count)
        {
            int[] triangles = new int[(count * (length - 1)) * 6];
            int k = 0;
            if (count == 1)
            {
                for (int i = 0; i < length-1; i++)
                {
                    int a = i * 2;
                    triangles[k] = a;
                    triangles[k + 1] = a + 1;
                    triangles[k + 2] = a + 3;
                    triangles[k + 3] = a;
                    triangles[k + 4] = a + 3;
                    triangles[k + 5] = a + 2;
                    k += 6;
                }
            }
            else
            {
                for (int i = 0; i < length - 1; i++)
                {
                    
                    for (int j = 0; j < count; j++)
                    {
                        if (j == count - 1)
                        {
                            Debug.Log("k=" + k);
                            triangles[k] = (i * count) + j;
                            triangles[k + 1] = (i * count) + 0;
                            triangles[k + 2] = ((i + 1) * count) + 0;
                            triangles[k + 3] = (i * count) + j;
                            triangles[k + 4] = ((i + 1) * count) + 0;
                            triangles[k + 5] = ((i + 1) * count) + j;
                        }
                        else
                        {
                            triangles[k] = (i * count) + j;
                            triangles[k + 1] = (i * count) + j + 1;
                            triangles[k + 2] = ((i + 1) * count) + j + 1;
                            triangles[k + 3] = (i * count) + j;
                            triangles[k + 4] = ((i + 1) * count) + j + 1;
                            triangles[k + 5] = ((i + 1) * count) + j;
                        }
                        k += 6;
                    }


                }
            }
           
            return triangles;
        }
        #endregion
    }
}
